const express = require("express");
const app = express();
const { MongoClient, ObjectId } = require("mongodb");

const connectUri = "mongodb://localhost:27017";
const client = new MongoClient(connectUri, { useUnifiedTopology: true });

// 配置解析请求体数据 application/json
// 它会把解析到的请求体数据放到 req.body 中
// 注意：一定要在使用之前就挂载这个中间件
app.use(express.json());

app.get("/", (req, res) => {
  res.send("Hello World!");
});

// 创建文章
app.post("/articles", async (req, res, next) => {
  try {
    // 1. 获取客户端表单数据
    const { article } = req.body;
    // 2. 数据校验
    if (!article || !article.title || !article.description || !article.body) {
      return res.status(422).json({
        error: "缺少必要参数",
      });
    }
    await client.connect();
    const db = client.db("test");
    const articlesCollection = db.collection("articles");
    article.createdAt = new Date();
    article.updatedAt = new Date();
    // 3. 把数据防止数据库中
    const ret = await articlesCollection.insertOne(article);
    article._id = ret.insertedId;
    // 4. 响应客户端
    return res.status(200).json({
      article,
    });
  } catch (error) {
    // 由错误中间件统一处理
    next(error);
  }
});

// 获取文章列表
app.get("/articles", async (req, res, next) => {
  try {
    let { _page = 1, _size = 10 } = req.query;
    _page = Number.parseInt(_page);
    _size = Number.parseInt(_size);
    if (_page < 1 || _size < 1) {
      return res.status(422).json({
        error: "分页参数错误",
      });
    }
    await client.connect();
    const db = client.db("test");
    const articlesCollection = db.collection("articles");
    const articles = await articlesCollection
      .find()
      .skip((_page - 1) * _size)
      .limit(_size)
      .toArray();
    // 获取文章总数
    const articlesCount = await articlesCollection.countDocuments();
    res.status(200).json({
      articles,
      articlesCount,
    });
  } catch (error) {
    next(error);
  }
});

// 获取单篇文章
app.get("/articles/:id", async (req, res, next) => {
  try {
    const { id } = req.params;
    await client.connect();
    const db = client.db("test");
    const articlesCollection = db.collection("articles");
    const article = await articlesCollection.findOne({ _id: ObjectId(id) });
    if (!article) {
      return res.status(404).json({
        error: "文章不存在",
      });
    }
    res.status(200).json({
      article,
    });
  } catch (error) {
    next(error);
  }
});

app.patch("/articles/:id", async (req, res, next) => {
  try {
    const { id } = req.params;
    await client.connect();
    const db = client.db("test");
    const articlesCollection = db.collection("articles");
    await articlesCollection.updateOne(
      { _id: ObjectId(id) },
      { $set: req.body.article }
    );

    const article = await articlesCollection.findOne({
      _id: ObjectId(id),
    });
    res.status(200).json({
      article,
    });
  } catch (error) {
    next(error);
  }
});

app.delete("/articles/:id", async (req, res, next) => {
  try {
    const { id } = req.params;
    await client.connect();
    const db = client.db("test");
    const articlesCollection = db.collection("articles");
    await articlesCollection.deleteOne({ _id: ObjectId(id) });
    res.status(200).json({
      message: "删除成功",
    });
  } catch (error) {
    next(error);
  }
});

// 错误处理中间件(四个参数必须给全，不然不认为是一个错误中间件)
app.use((err, req, res, next) => {
  res.status(500).json({
    error: err.message,
  });
});

app.listen(3000, () => {
  console.log("Example app listening on port 3000!");
});
